/**
 * 专门负责储存所有的物理页，和物理页页号与物理页地址间的相互转换。
 */
#ifndef _AHAMOLETT_PAGES_H_
#define _AHAMOLETT_PAGES_H_

#include <mm/mm_types.h>

/**
 * @brief 创建并初始化储存所有内存页结构体的数组。
 * 
 * 在做内存页结构体的操作之前，必须执行该函数进行初始化。且在执行该初始化操作前，需要先对
 * 前期内存分配器进行初始化。
 * 
 * 该函数会丢弃掉给定区域两边没有与页框对齐的部分。
 * 
 * @param physical_start 整个内存中第一个可分配内存区域的首地址。
 * @param physical_end 整个内存中最后一个可分配内存区域的末尾地址。
 * @param kernel_offset 恒等映射区中物理地址到虚拟地址的偏移，必须按页框对齐。
 * 
 * @return int 初始化成功返回0，失败返回-1。
 */
extern int pages_init(
    unsigned long physical_start,
    unsigned long physical_end,
    unsigned long kernel_offset
);

/**
 * @brief 由物理地址到页帧号的转换。
 * 
 * @param physical unsigned long 物理地址。
 * 
 * @return unsigned long 页帧号。
 */
#define physical_to_pfn(physical)   ((physical) >> PAGE_SHIFT)

/**
 * @brief 获得第一个有效内存页的结构体。
 * 
 * 不会检查页结构体是否有效。
 * 
 * @return struct page_struct* 找到返回结构体的指针，否则返回NULL。
 */
extern struct page_struct *get_first_page(void);

/**
 * @brief 获得最后一个有效内存页的结构体。
 * 
 * 不会检查页结构体是否有效。
 * 
 * @return struct page_struct* 找到返回结构体的指针，否则返回NULL。
 */
extern struct page_struct *get_last_page(void);

/**
 * @brief 由物理地址获得内存页结构体。
 * 
 * @param physical 物理地址。
 * 
 * @return struct page_struct* 物理地址在范围内返回结构体的指针，否则返回NULL。
 */
extern struct page_struct *physical_to_page(unsigned long physical);
/* 通过pfn获得页结构体。 */
#define pfn_to_page(pfn)    physical_to_page((pfn) << PAGE_SHIFT)
/* 由恒等映射区的虚拟地址获得页结构体 */
#define virtual_to_page(virtual)    physical_to_page(VIRT_TO_PHY(virtual))

/**
 * @brief 获得内存页结构体的物理地址。
 * 
 * @param page 物理页结构体的指针。
 * 
 * @return unsigned long 传入的结构体有效返回对应的物理地址，否则返回NULL。
 */
extern unsigned long get_page_physical(struct page_struct *page);
/* 获得内存页结构体的页帧号。 */
#define get_page_pfn(page)  (get_page_physical(page) >> PAGE_SHIFT)
/* 获得内存页结构体在线性映射区中的虚拟地址。 */
#define get_page_virtual(page)  PHY_TO_VIRT(get_page_physical(page))

/**
 * @brief 测试一个页结构体是否有效。
 * 
 * @param page 物理页结构体的指针。
 * 
 * @retval true 页结构体有效。
 * @retval false 页结构体无效效。
 */
extern int is_page_valid(struct page_struct *page);

/**
 * @brief 将给定内存区域内所有内存页对应的结构体设置为有效状态。
 * 
 * 该函数不会设置给定区域两边没有与页框对齐的部分。
 * 
 * @param physical_start 内存区域物理地址的首地址。
 * @param physical_end 内存区域物理地址的末尾地址。
 * 
 * @return int 设置成功返回0，失败返回-1。
 */
extern int set_pages_valid(
    unsigned long physical_start,
    unsigned long physical_end
);

/**
 * @brief 得到有效的页结构体数量。
 * 
 * @return unsigned long 有效的页结构体数量。
 */
extern unsigned long get_valig_pages_num(void);

/**
 * @brief 获得整个内存的起始页帧号。
 * 
 * @return unsigned long 整个内存的起始页帧号。
 */
extern unsigned long get_start_pfn(void);

/**
 * @brief 获得范围内有效内存页的数量。
 * 
 * @return unsigned long 范围内有效内存页的数量。
 */
extern unsigned long get_spanned_pages_num(void);

/**
 * @brief 初始化单个内存页结构体。
 * 
 * 创建储存所有内存页结构体的数组时，所有内存页都会被设置为无效状态。通过该函数，可以将结构体
 * 设置为有效状态。
 * 
 * @param page 进行初始化的内存页结构体。
 * @param physical 内存页的物理地址首地址。
 * 
 * @return struct page_struct* 初始化成功返回被初始化的结构体，失败返回NULL。
 */
extern struct page_struct *page_struct_init(
    struct page_struct *page,
    unsigned long physical
);

/**
 * @brief 自旋等待直到最终获得某个内存页的自旋锁。
 * 
 * @param page 物理页结构体的指针。
 */
extern void lock_page_struct(struct page_struct *page);

/**
 * @brief 释放某个内存页的自旋锁。
 * 
 * @param page 物理页结构体的指针。
 */
extern void unlock_page_struct(struct page_struct *page);

/**
 * @brief 检查某个内存页的自旋锁是否已经被占有。
 * 
 * @param page 物理页结构体的指针。
 * 
 * @retval true 页被锁或页无效
 * @retval false 页没被锁。
 */
extern int page_struct_is_locked(struct page_struct *page);

#endif
